package mn.idax.exchange.otc.service.impl;

import com.alibaba.fastjson.JSON;
import mn.idax.exchange.common.bean.ResponseData;
import mn.idax.exchange.otc.domain.capital.*;
import mn.idax.exchange.otc.entity.*;
import mn.idax.exchange.otc.enums.AccountTransactionEnum;
import mn.idax.exchange.otc.enums.CapitalAPIEnum;
import mn.idax.exchange.otc.enums.response.OTCAdResponseEnum;
import mn.idax.exchange.otc.enums.response.OrderResponseEnum;
import mn.idax.exchange.otc.exception.IdaxException;
import mn.idax.exchange.otc.mapper.AccountMapper;
import mn.idax.exchange.otc.mapper.AccountTransactionMapper;
import mn.idax.exchange.otc.mapper.CoinMapper;
import mn.idax.exchange.otc.mapper.RequestLogMapper;
import mn.idax.exchange.otc.service.AccountCapitalService;
import mn.idax.exchange.otc.service.CapitalAPIBaseService;
import mn.idax.exchange.otc.util.JsonUtil;
import mn.idax.exchange.otc.util.LogUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static mn.idax.exchange.otc.enums.response.OTCAdResponseEnum.COIN_ACCOUNT_NOT_ENOUGH;

/**
 * V3.0账户资金服务接口实现
 *
 * @author .shouhua
 * @time 2018.11.21
 */
@Service
public class AccountCapitalServiceImpl extends CapitalAPIBaseService implements AccountCapitalService {

    @Autowired
    private RequestLogMapper requestLogMapper;
    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountTransactionMapper accountTransactionMapper;
    @Autowired
    private CoinMapper coinMapper;

    @Override
    public Account getBalances(Integer userId, Integer coinId) {

        return accountMapper.selectByUserIdCoinId(userId, coinId);
    }

    @Override
    public Account getBalances(Integer userId, String coinCode) {
        return accountMapper.selectByUserIdCoinCode(userId, coinCode);
    }

    /**
     * 构造请求核心参数
     *
     * @param changeListData
     * @param changeAccountEnum
     * @param changeAccountDTO
     */
    @Override
    public RequestLogSimple makeChangeAccountParams(ChangeListData changeListData, CapitalAPIEnum changeAccountEnum, RequestLogSimple requestLogSimple, ChangeAccountDTO changeAccountDTO) {
        LogUtils.info(this.getClass(), "changeListData : " + JSON.toJSONString(changeListData));
        LogUtils.info(this.getClass(), "makeChangeAccountParams  changeAccountDTO: " + JSON.toJSONString(changeAccountDTO));

        //set txId and txName
        changeListData.setMustParam(changeAccountEnum.getTxId(), changeAccountEnum.getTxName());

        requestLogSimple.setGuid(getGuidByRedis(changeAccountEnum));

        //copy properties and set guid
        BeanUtils.copyProperties(changeListData, changeAccountDTO);

        LogUtils.info(this.getClass(), "changeAccountDTO : " + JSON.toJSONString(changeAccountDTO));

        changeAccountDTO.setGuid(requestLogSimple.getGuid());
        changeAccountDTO.setReverse(0);

        // 正向请求Json
        requestLogSimple.setRequestJson(JSON.toJSONString(changeAccountDTO));

        // 反向请求Json
        changeAccountDTO.setAvailableChange(changeAccountDTO.getAvailableChange().multiply(new BigDecimal("-1")));
        changeAccountDTO.setFrozenChange(changeAccountDTO.getFrozenChange().multiply(new BigDecimal("-1")));
        changeAccountDTO.setReverse(1);
        requestLogSimple.setReverseJson(JSON.toJSONString(changeAccountDTO));

        return requestLogSimple;
    }

    @Override
    public Boolean changeAccount(ChangeListData changeListData, CapitalAPIEnum changeAccountEnum) {

        Account account = accountMapper.selectByPrimaryKey(changeListData.getAccountId());
        if (Objects.isNull(account)) {
            throw new IdaxException(OTCAdResponseEnum.TRANSACTION_CURRENT_NOT_EXIST.getCode());
        }

        Coin coin = coinMapper.selectByPrimaryKey(account.getCoinid());

        account.setAvailableamount(account.getAvailableamount().add(changeListData.getAvailableChange()));
        account.setFrozenamount(account.getFrozenamount().add(changeListData.getFrozenChange()));
        if (account.getAvailableamount().compareTo(BigDecimal.ZERO) == -1 || account.getFrozenamount().compareTo(BigDecimal.ZERO) == -1) {
            throw new IdaxException(OTCAdResponseEnum.COIN_ACCOUNT_NOT_ENOUGH.getCode());
        }
        // 修改金额
        accountMapper.changeAccount(account);
        // 增加记录
        AccountTransaction accountTransaction = new AccountTransaction(account.getId(), changeListData.getUserId(), changeListData.getTransactionType(), changeListData.getAvailableChange(), changeListData.getFrozenChange(), account.getAvailableamount(), account.getFrozenamount(), BigDecimal.ZERO, "", coin.getCode(), new Date(), new Date());
        accountTransactionMapper.insert(accountTransaction);

        return true;

    }

    /**
     * 资金划转专用
     *
     * @param order
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = {RuntimeException.class, Exception.class})
    public RequestLogSimple makeTransferCapitalData(OTCOrder order, CapitalAPIEnum changeAccountEnum, RequestLogSimple requestLogSimple) {
        CapitalAPIEnum completeOrderEnum = CapitalAPIEnum.COMPLETE_ORDER;
        requestLogSimple.setGuid(getGuidByRedis(completeOrderEnum));

        Integer buyer = order.getBuyer();
        Integer seller = order.getSeller();
        Integer coinid = order.getCoinid();

        Account buyerAccount = this.getBalances(buyer, coinid);
        Account sellerAccount = this.getBalances(seller, coinid);
        LogUtils.info(this.getClass(), "release sellerAccount is " + JSON.toJSONString(sellerAccount));
        if (sellerAccount == null || order.getQty().compareTo(sellerAccount.getFrozenamount()) > 0) {
            LogUtils.info(this.getClass(), "makeTransferCapitalData account: " + JSON.toJSONString(sellerAccount) + "orderInfo: " + JSON.toJSONString(order));
            throw new IdaxException(OrderResponseEnum.SELLER_ACCOUNT_NOT_ENOUGH.getRespCode());
        }
        Integer type = AccountTransactionEnum.TRANSACTIONTYPE_RELEASEORDER.getCode();

        TransferAccountDTO transferBuyer = TransferAccountDTO.builder()
                .accountId(null == buyerAccount ? 0 : buyerAccount.getId())
                .userId(buyer)
                .transactionType(type)
                .remark(AccountTransactionEnum.TRANSACTIONTYPE_RELEASEORDER.getRemark())
                .availableChange(order.getQty())
                .frozenChange(new BigDecimal("0"))
                .build();
        LogUtils.info(this.getClass(), "OTCReleaseTransferBuyer:" + transferBuyer.toString());

        TransferAccountDTO transferSeller = TransferAccountDTO.builder()
                .accountId(sellerAccount.getId())
                .userId(seller)
                .transactionType(type)
                .remark(AccountTransactionEnum.TRANSACTIONTYPE_RELEASEORDER.getRemark())
                .availableChange(new BigDecimal("0"))
                .frozenChange(order.getQty().multiply(new BigDecimal("-1")))
                .build();
        LogUtils.info(this.getClass(), "OTCReleaseTransferSeller:" + transferSeller.toString());

        TransferCapitalDTO transferCapitalDTO = TransferCapitalDTO.builder()
                .guid(requestLogSimple.getGuid())
                .txId(CapitalAPIEnum.COMPLETE_ORDER.getTxId())
                .txName(CapitalAPIEnum.COMPLETE_ORDER.getTxName())
                .coinId(order.getCoinid())
                .coinCode(order.getPairname())
                .sourceId(order.getId())
                .sellerAccount(transferSeller)
                .buyerAccount(transferBuyer)
                .build();
        LogUtils.info(this.getClass(), "OTCReleaseTransferCapitalDTO:" + transferCapitalDTO.toString());

        // 正向请求
        requestLogSimple.setRequestJson(JsonUtil.writeValue(transferCapitalDTO));

        // 反向请求
        // 买家
        transferCapitalDTO.getBuyerAccount().setAvailableChange(transferCapitalDTO.getBuyerAccount().getAvailableChange().multiply(new BigDecimal("-1")));
        transferCapitalDTO.getBuyerAccount().setFrozenChange(transferCapitalDTO.getBuyerAccount().getFrozenChange().multiply(new BigDecimal("-1")));
        // 卖家
        transferCapitalDTO.getSellerAccount().setAvailableChange(transferCapitalDTO.getSellerAccount().getAvailableChange().multiply(new BigDecimal("-1")));
        transferCapitalDTO.getSellerAccount().setFrozenChange(transferCapitalDTO.getSellerAccount().getFrozenChange().multiply(new BigDecimal("-1")));
        transferCapitalDTO.setReverse(1);
        requestLogSimple.setReverseJson(JsonUtil.writeValue(transferCapitalDTO));
        return requestLogSimple;

    }

    @Override
    public Boolean transferCapital(OTCOrder order) {
        if (order == null) {
            throw new IdaxException(OrderResponseEnum.ORDER_ISNOTEXIST.getRespCode());
        }

        CapitalAPIEnum completeOrderEnum = CapitalAPIEnum.COMPLETE_ORDER;
        Long guid = getGuidByRedis(completeOrderEnum);

        Integer buyer = order.getBuyer();
        Integer seller = order.getSeller();
        Integer coinid = order.getCoinid();

        Account buyerAccount = this.getBalances(buyer, coinid);
        Account sellerAccount = this.getBalances(seller, coinid);
        LogUtils.info(this.getClass(), "release sellerAccount is " + JSON.toJSONString(sellerAccount));
        if (sellerAccount == null || order.getQty().compareTo(sellerAccount.getFrozenamount()) > 0) {
            LogUtils.info(this.getClass(), "transferCapital account: " + JSON.toJSONString(sellerAccount) + "orderInfo: " + JSON.toJSONString(order));
            throw new IdaxException(OrderResponseEnum.SELLER_ACCOUNT_NOT_ENOUGH.getRespCode());
        }
        Integer type = AccountTransactionEnum.TRANSACTIONTYPE_RELEASEORDER.getCode();

        TransferAccountDTO transferBuyer = TransferAccountDTO.builder()
                .accountId(null == buyerAccount ? 0 : buyerAccount.getId())
                .userId(buyer)
                .transactionType(type)
                .remark(AccountTransactionEnum.TRANSACTIONTYPE_RELEASEORDER.getRemark())
                .availableChange(order.getQty())
                .frozenChange(new BigDecimal("0"))
                .build();
        LogUtils.info(this.getClass(), "OTC 放币transferBuyer:" + transferBuyer.toString());

        TransferAccountDTO transferSeller = TransferAccountDTO.builder()
                .accountId(sellerAccount.getId())
                .userId(seller)
                .transactionType(type)
                .remark(AccountTransactionEnum.TRANSACTIONTYPE_RELEASEORDER.getRemark())
                .availableChange(new BigDecimal("0"))
                .frozenChange(order.getQty().multiply(new BigDecimal("-1")))
                .build();
        LogUtils.info(this.getClass(), "OTC 放币transferSeller:" + transferSeller.toString());

        TransferCapitalDTO transferCapitalDTO = TransferCapitalDTO.builder()
                .guid(guid)
                .txId(CapitalAPIEnum.COMPLETE_ORDER.getTxId())
                .txName(CapitalAPIEnum.COMPLETE_ORDER.getTxName())
                .coinId(order.getCoinid())
                .coinCode(order.getPairname())
                .sourceId(order.getId())
                .sellerAccount(transferSeller)
                .buyerAccount(transferBuyer)
                .build();
        LogUtils.info(this.getClass(), "OTC 放币transferCapitalDTO:" + transferCapitalDTO.toString());

        // 正向请求
        String requestJson = JsonUtil.writeValue(transferCapitalDTO);

        // 反向请求
        // 买家
        transferCapitalDTO.getBuyerAccount().setAvailableChange(transferCapitalDTO.getBuyerAccount().getAvailableChange().multiply(new BigDecimal("-1")));
        transferCapitalDTO.getBuyerAccount().setFrozenChange(transferCapitalDTO.getBuyerAccount().getFrozenChange().multiply(new BigDecimal("-1")));
        // 卖家
        transferCapitalDTO.getSellerAccount().setAvailableChange(transferCapitalDTO.getSellerAccount().getAvailableChange().multiply(new BigDecimal("-1")));
        transferCapitalDTO.getSellerAccount().setFrozenChange(transferCapitalDTO.getSellerAccount().getFrozenChange().multiply(new BigDecimal("-1")));
        transferCapitalDTO.setReverse(1);
        String reverseJson = JsonUtil.writeValue(transferCapitalDTO);

        // 调用API修改资金
        return compenstateRequestAPI(guid, completeOrderEnum, requestJson, reverseJson, order.getId());

    }


    public Account linkHashMapToAccount(Map<String, Object> map, Integer userId, Integer coinId) {
        return Account.builder()
                .id((Integer) map.get("id"))
                .userid((Integer) map.get("userId"))
                .coinid((Integer) map.get("coinId"))
                .availableamount(new BigDecimal(map.get("availableamount").toString()))
                .frozenamount(new BigDecimal(map.get("frozenamount").toString()))
//                .createtime(DateUtils.getDate(map.get("createTime").toString(), "yyyyMMdd"))
//                .updatetime(DateUtils.getDate(map.get("updateTime").toString(), "yyyyMMdd"))
                .createtime(null)
                .updatetime(null)
                .build();
    }
}
